x402 Facilitator Reference
Detailed API reference for @x402-avm/core/facilitator, @x402-avm/avm/exact/facilitator, and @x402-avm/extensions packages.
Package: @x402-avm/core
Installation
npm install @x402-avm/core @x402-avm/avm algosdk
Facilitator Exports from @x402-avm/core/facilitator
| Export | Type | Description |
|---|---|---|
x402Facilitator |
Class | Core facilitator for verifying and settling payments |
Server Exports from @x402-avm/core/server
| Export | Type | Description |
|---|---|---|
x402ResourceServer |
Class | Transport-agnostic resource server |
x402HTTPResourceServer |
Class | HTTP-aware resource server with route config |
HTTPFacilitatorClient |
Class | Client for communicating with a remote facilitator |
ResourceConfig |
Type | Resource payment configuration |
RouteConfig |
Type | HTTP route configuration |
x402Facilitator Class
The core facilitator that verifies payment validity and settles transactions on-chain.
Methods
| Method | Signature | Description |
|---|---|---|
verify |
(payload, requirements) => Promise<VerifyResult> |
Verify a payment payload without submitting |
settle |
(payload, requirements) => Promise<SettleResult> |
Verify and settle a payment on-chain |
getSupportedNetworks |
() => SupportedNetworks |
Return all registered networks |
onBeforeVerify |
(hook) => void |
Register pre-verification hook |
onAfterVerify |
(hook) => void |
Register post-verification hook |
onBeforeSettle |
(hook) => void |
Register pre-settlement hook |
onAfterSettle |
(hook) => void |
Register post-settlement hook |
verify()
Validates a payment payload against requirements without submitting to the network. Checks:
- Transaction group structure
- Amounts and recipients
- Timing (not expired)
- Signatures (via simulation)
const result = await facilitator.verify(paymentPayload, paymentRequirements);
// result: { isValid: boolean, error?: string }
settle()
Verifies the payment, co-signs the fee-payer transaction, and submits the group to the network.
const result = await facilitator.settle(paymentPayload, paymentRequirements);
// result: { success: boolean, txId?: string, error?: string }
FacilitatorAvmSigner Interface
The interface that bridges the facilitator to the Algorand blockchain.
interface FacilitatorAvmSigner {
/**
* Returns the list of Algorand addresses this facilitator controls.
* Used to identify which transactions in a group the facilitator should sign.
*/
getAddresses(): string[];
/**
* Sign a single unsigned transaction.
* @param txn - Raw unsigned transaction bytes (msgpack)
* @param senderAddress - The sender address of the transaction
* @returns Signed transaction bytes
*/
signTransaction(txn: Uint8Array, senderAddress: string): Promise<Uint8Array>;
/**
* Get an Algodv2 client configured for the specified network.
* @param network - CAIP-2 network identifier
* @returns Configured algosdk.Algodv2 instance
*/
getAlgodClient(network: string): algosdk.Algodv2;
/**
* Simulate a transaction group for verification without submission.
* Must handle both signed and unsigned transactions in the group.
* Use allowEmptySignatures: true for unsigned transactions.
* @param txns - Array of transaction bytes (mixed signed/unsigned)
* @param network - CAIP-2 network identifier
* @returns Simulation result
*/
simulateTransactions(txns: Uint8Array[], network: string): Promise<any>;
/**
* Send signed transactions to the Algorand network.
* All transactions must be fully signed before calling this method.
* Concatenate bytes with Buffer.concat() before sendRawTransaction.
* @param signedTxns - Array of signed transaction bytes
* @param network - CAIP-2 network identifier
* @returns Transaction ID
*/
sendTransactions(signedTxns: Uint8Array[], network: string): Promise<string>;
/**
* Wait for a transaction to be confirmed on the network.
* @param txId - Transaction ID to wait for
* @param network - CAIP-2 network identifier
* @param waitRounds - Number of rounds to wait (default: 4)
* @returns Confirmation result
*/
waitForConfirmation(
txId: string,
network: string,
waitRounds?: number,
): Promise<any>;
}
Implementation Notes
simulateTransactions: Must handle mixed signed/unsigned transaction bytes. Wrap unsigned transactions with new algosdk.SignedTransaction({ txn }):
simulateTransactions: async (txns, network) => {
const stxns = txns.map((txnBytes) => {
try {
return algosdk.decodeSignedTransaction(txnBytes);
} catch {
const txn = algosdk.decodeUnsignedTransaction(txnBytes);
return new algosdk.SignedTransaction({ txn });
}
});
// ...
}
sendTransactions: Concatenate all signed transaction bytes before sending:
sendTransactions: async (signedTxns, network) => {
const combined = Buffer.concat(signedTxns.map(t => Buffer.from(t)));
const { txId } = await algodClient.sendRawTransaction(combined).do();
return txId;
}
Subpath: @x402-avm/avm/exact/facilitator
registerExactAvmScheme (Facilitator)
Registers the AVM exact payment scheme with a facilitator instance.
import { registerExactAvmScheme } from "@x402-avm/avm/exact/facilitator";
function registerExactAvmScheme(
facilitator: x402Facilitator,
config: {
signer: FacilitatorAvmSigner;
networks: string | string[];
},
): void;
Note: The registerExactAvmScheme function exists at three import paths with different signatures:
@x402-avm/avm/exact/client-- For client-side (takesClientAvmSigner)@x402-avm/avm/exact/server-- For resource server (no signer needed)@x402-avm/avm/exact/facilitator-- For facilitator (takesFacilitatorAvmSigner)
Package: @x402-avm/extensions
Installation
npm install @x402-avm/extensions @x402-avm/core
Bazaar Discovery Exports
| Export | Type | Description |
|---|---|---|
BAZAAR |
Constant | Extension key string |
declareDiscoveryExtension |
Function | Declares discovery metadata for a resource |
bazaarResourceServerExtension |
Object | Resource server extension that enriches declarations |
extractDiscoveryInfo |
Function | Extracts discovery info from payment flow |
validateDiscoveryExtension |
Function | Validates a discovery extension object |
validateAndExtract |
Function | Validates and extracts in one step |
extractDiscoveryInfoFromExtension |
Function | Lower-level extraction from extension object |
withBazaar |
Function | Extends a facilitator client with Bazaar query capabilities |
WithExtensions |
Type | Type utility for chaining extensions |
DiscoveryInfo |
Type | Discovery information structure |
DiscoveredResource |
Type | Fully extracted resource with metadata |
ValidationResult |
Type | Result of extension validation |
DiscoveryResourcesResponse |
Type | Response from discovery query |
DiscoveryResource |
Type | Single discovered resource entry |
ListDiscoveryResourcesParams |
Type | Query parameters for listing resources |
BazaarClientExtension |
Type | Extension methods added by withBazaar |
declareDiscoveryExtension
Creates discovery metadata to include in the PaymentRequired response.
function declareDiscoveryExtension(config: {
/** Example input values (query params or body) */
input?: Record<string, any>;
/** JSON Schema for validating input */
inputSchema?: object;
/** Body type for POST endpoints */
bodyType?: "json" | "form-data";
/** Output specification */
output?: {
/** Example response */
example?: any;
/** JSON Schema for output */
schema?: object;
};
}): { bazaar: { info: DiscoveryInfo; schema: object } };
Discovery Info Structure
V2 format includes:
info: Contains the actual discovery data (method, params, output examples)schema: JSON Schema that validates the structure ofinfo
V1 compatibility: V1 data stored in PaymentRequirements.outputSchema is automatically transformed to V2 DiscoveryInfo format.
extractDiscoveryInfo
Extracts discovery information from a payment flow. Handles both V2 and V1 formats automatically.
function extractDiscoveryInfo(
paymentPayload: PaymentPayload,
paymentRequirements: PaymentRequirements,
validate?: boolean, // default: true
): DiscoveredResource | null;
DiscoveredResource
interface DiscoveredResource {
/** URL of the discovered resource */
resourceUrl: string;
/** HTTP method (GET, POST, etc.) */
method: string;
/** x402 protocol version (1 or 2) */
x402Version: number;
/** Human-readable description */
description: string;
/** Response MIME type */
mimeType: string;
/** Full discovery information */
discoveryInfo: DiscoveryInfo;
}
bazaarResourceServerExtension
A resource server extension that enriches discovery declarations at request time by narrowing the HTTP method to the actual method used.
// Register on resource server
httpServer.resourceServer.registerExtension(bazaarResourceServerExtension);
When the resource server builds a PaymentRequired response, this extension's enrichDeclaration hook automatically sets the method field based on the incoming HTTP request method.
withBazaar
Extends an HTTPFacilitatorClient with Bazaar discovery query capabilities.
function withBazaar<T extends HTTPFacilitatorClient>(
client: T,
): T & { extensions: { discovery: BazaarClientExtension } };
BazaarClientExtension
interface BazaarClientExtension {
listResources(params?: ListDiscoveryResourcesParams): Promise<DiscoveryResourcesResponse>;
}
ListDiscoveryResourcesParams
interface ListDiscoveryResourcesParams {
/** Filter by resource type (e.g., "http") */
type?: string;
/** Maximum number of results */
limit?: number;
/** Offset for pagination */
offset?: number;
}
DiscoveryResourcesResponse
interface DiscoveryResourcesResponse {
items: DiscoveryResource[];
pagination: {
limit: number;
offset: number;
total: number;
};
}
WithExtensions Type Utility
Properly merges extension types when chaining multiple extensions on a client.
type WithExtensions<T, E> = T extends { extensions: infer Existing }
? Omit<T, "extensions"> & { extensions: Existing & E }
: T & { extensions: E };
When T has no existing extensions, the result is T & { extensions: E }. When T already has extensions (from chaining), the new extension type is merged with existing ones.
Lifecycle Hooks
Facilitator Hooks
facilitator.onBeforeVerify(async (context) => {
// context.paymentPayload, context.requirements
});
facilitator.onAfterVerify(async (context) => {
// context.result - verification result
});
facilitator.onBeforeSettle(async (context) => {
// context.paymentPayload, context.requirements
});
facilitator.onAfterSettle(async (context) => {
// context.result - settlement result
// context.paymentPayload, context.requirements
// Good place to extract discovery info for Bazaar cataloging
});
Environment Variables
| Variable | Description | Format |
|---|---|---|
AVM_PRIVATE_KEY |
Facilitator's Algorand private key | Base64-encoded 64-byte key (32-byte seed + 32-byte pubkey) |
ALGOD_SERVER |
Algod server URL | URL string |
ALGOD_TOKEN |
Algod API token | String |
ALGOD_TESTNET_URL |
Custom testnet Algod URL | URL (default: https://testnet-api.algonode.cloud) |
ALGOD_MAINNET_URL |
Custom mainnet Algod URL | URL (default: https://mainnet-api.algonode.cloud) |
FACILITATOR_URL |
URL of the facilitator service | URL string |
FACILITATOR_API_KEY |
API key for authenticated facilitator access | String |
Network Constants
| Constant | Value |
|---|---|
ALGORAND_TESTNET_CAIP2 |
"algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=" |
ALGORAND_MAINNET_CAIP2 |
"algorand:wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8=" |
USDC_TESTNET_ASA_ID |
"10458941" |
USDC_MAINNET_ASA_ID |
"31566704" |
Testing
Testing a Facilitator Locally
# Start the facilitator
AVM_PRIVATE_KEY="your-base64-key" npx tsx facilitator.ts
# Check supported networks
curl http://localhost:4000/supported
# Verify a payment (requires valid payload)
curl -X POST http://localhost:4000/verify \
-H "Content-Type: application/json" \
-d '{"paymentPayload": {...}, "paymentRequirements": {...}}'
# Settle a payment
curl -X POST http://localhost:4000/settle \
-H "Content-Type: application/json" \
-d '{"paymentPayload": {...}, "paymentRequirements": {...}}'
Testing Bazaar Discovery
# List all discovered resources
curl http://localhost:4000/discovery/resources
# Filter by type
curl "http://localhost:4000/discovery/resources?type=http"
# With pagination
curl "http://localhost:4000/discovery/resources?limit=10&offset=0"
Important Notes
- The facilitator address must have ALGO to cover transaction fees. Each settlement requires a minimum of 0.001 ALGO for the fee-payer transaction.
simulateTransactionsmust handle both signed and unsigned transaction bytes in the same group. The client signs their payment transaction; the facilitator's fee-payer transaction arrives unsigned.sendTransactionsreceives an array of individually signed transaction bytes. Concatenate them withBuffer.concat()before callingsendRawTransaction.- The
registerExactAvmSchemefunction at@x402-avm/avm/exact/facilitatoris distinct from the client and server variants. Always import from the correct subpath. - Bazaar discovery is purely additive. If no extensions are present in the payment payload, the facilitator operates normally without cataloging.
- The
withBazaarfunction mutates the client in place and adds anextensions.discoverynamespace. It is safe to chain with other extension wrappers.