SAP Explorer Docs
SDK Reference

Escrow API

Low-level and high-level APIs for x402 escrow management, settlement, and balance tracking.

Escrow API

The SDK provides two layers for managing x402 escrow payments: the high-level X402Registry and the low-level EscrowModule.

X402Registry (High-Level)

Access via client.x402. Handles cost estimation, payment preparation, header generation, settlement, and balance tracking.

Estimate Cost

// estimateCost reads the agent's on-chain pricing tiers and volume curve
// to calculate the total cost for a given number of calls.
// Returns: { totalCost, effectivePricePerCall, hasVolumeCurve, tiers }
const estimate = await client.x402.estimateCost(
  agentWallet,  // PublicKey: the agent you want to pay
  100,          // number of calls you plan to make
);

console.log(estimate.totalCost.toString());           // BN → string
console.log(estimate.effectivePricePerCall.toString()); // weighted average
console.log(estimate.hasVolumeCurve);                 // has tiered pricing?

Prepare Payment

Creates escrow and deposits funds in a single transaction:

// preparePayment sends one Solana TX: creates escrow PDA + deposits lamports.
// Returns a PaymentContext with all the derived PDAs needed for headers.
const ctx = await client.x402.preparePayment(agentWallet, {
  pricePerCall: 1_000,           // lamports per call (number, string, or BN)
  maxCalls: 500,                 // max calls allowed (0 = unlimited)
  deposit: 500_000,              // initial deposit in lamports
  expiresAt: 0,                  // Unix timestamp (0 = never expires)
  volumeCurve: [                 // optional: tiered pricing breakpoints
    { afterCalls: 100, pricePerCall: 800 },  // 20% discount after 100 calls
  ],
});
OptionTypeDefaultDescription
pricePerCallnumber or string or BNRequiredBase price per call
maxCallsnumber or string or BN0Max calls (0 = unlimited)
depositnumber or string or BNRequiredInitial deposit amount
expiresAtnumber or string or BN0Unix timestamp (0 = never)
volumeCurveArrayEmptyVolume discount breakpoints
tokenMintPublicKey or nullnullSPL token mint
tokenDecimalsnumber9Token decimal places

Build Headers

// From a PaymentContext (returned by preparePayment)
const headers = client.x402.buildPaymentHeaders(ctx);
// Returns: { "X-Payment-Protocol", "X-Payment-Escrow", "X-Payment-Depositor", ... }

// From an existing escrow (looks up the PDA on-chain)
const headers = await client.x402.buildPaymentHeadersFromEscrow(agentWallet);

Settle

// Agent calls settle() to claim payment for calls served.
// depositorWallet: the client who funded the escrow (identifies which escrow)
// 5: number of calls to settle
// "service-data-v1": service data string (auto-hashed to SHA-256)
const receipt = await client.x402.settle(depositorWallet, 5, "service-data-v1");

Batch Settle

// settleBatch combines multiple settlements in one TX. Max 10 entries.
const batch = await client.x402.settleBatch(depositorWallet, [
  { calls: 3, serviceData: "batch-1" },  // settle 3 calls
  { calls: 7, serviceData: "batch-2" },  // settle 7 calls
]);

Balance

const balance = await client.x402.getBalance(agentWallet);
FieldTypeDescription
balanceBNCurrent remaining balance
totalDepositedBNCumulative deposits
totalSettledBNCumulative settlements
callsRemainingnumberRemaining calls
isExpiredbooleanExpiry check
affordableCallsnumberCalls the budget allows

Additional Methods

// Add more funds to an existing escrow (in lamports).
await client.x402.addFunds(agentWallet, 50_000);

// Withdraw funds from an escrow you own (must be the depositor).
await client.x402.withdrawFunds(agentWallet, 25_000);

// Close the escrow PDA. Balance must be 0. Reclaims ~0.004 SOL rent.
await client.x402.closeEscrow(agentWallet);

// Check if an escrow exists for this agent (returns boolean).
const exists = await client.x402.hasEscrow(agentWallet);

// Fetch the raw escrow account data (or null if not found).
const escrow = await client.x402.fetchEscrow(agentWallet);

// Verify a settlement TX by parsing its events.
const events = await client.x402.verifySettlement(txSignature);

EscrowModule (Low-Level)

Access via client.escrow. Direct Anchor instruction wrappers.

Create

import { BN } from "@coral-xyz/anchor";

// Low-level: all values must be BN. No automatic type conversion.
// Use the X402Registry (client.x402) for automatic conversions.
await client.escrow.create(agentWallet, {
  pricePerCall: new BN(1_000_000),     // 1,000,000 lamports per call
  maxCalls: new BN(100),               // max 100 calls (BN(0) = unlimited)
  initialDeposit: new BN(100_000_000), // deposit 0.1 SOL
  expiresAt: null,                     // null = never expires
  volumeCurve: null,                   // null = flat pricing
  tokenMint: null,                     // null = native SOL
  tokenDecimals: null,                 // null = defaults to 9 (SOL)
});

Deposit

await client.escrow.deposit(agentWallet, new BN(50_000_000));

Settle

import { sha256, hashToArray } from "@oobe-protocol-labs/synapse-sap-sdk/utils";

// Low-level settle: requires BN for calls and pre-hashed service data.
const serviceHash = hashToArray(sha256("service-record-001")); // number[32]
await client.escrow.settle(
  depositorWallet,  // PublicKey: the client's wallet
  new BN(5),        // number of calls to settle
  serviceHash,      // 32-byte SHA-256 hash
);

Batch Settle

// Low-level batch settle: each entry must use BN and pre-hashed service data.
// Max 10 entries per batch (LIMITS.MAX_BATCH_SETTLEMENTS).
await client.escrow.settleBatch(depositorWallet, [
  { callsToSettle: new BN(3), serviceHash: hashToArray(sha256("batch-1")) },
  { callsToSettle: new BN(7), serviceHash: hashToArray(sha256("batch-2")) },
]);

Withdraw and Close

// Withdraw partial funds (must be the depositor).
await client.escrow.withdraw(agentWallet, new BN(25_000_000));

// Close escrow PDA. Balance must be 0. Reclaims rent to payer.
await client.escrow.close(agentWallet);