Installation

npm install @hoodlayerorg/sdk

HoodLayerAgent

import { HoodLayerAgent } from "@hoodlayerorg/sdk";

const agent = new HoodLayerAgent(config: HoodLayerAgentConfig);

HoodLayerAgentConfig

Option Type Required Description
wallet Signer Yes A viem account, ethers wallet, or compatible signer
network "mainnet" \| "testnet" Yes Which Robinhood Chain network to use
rpcUrl string No A custom RPC endpoint for Robinhood Chain
facilitatorUrl string No Facilitator URL override (default: HoodLayer’s hosted Facilitator)
defaultToken string No Token address used by default (default: USDC)

Wallets

Two kinds of value work for wallet:

  • A viem local account or an ethers wallet
  • Anything that implements the Signer interface: { address: Address, signTypedData(typedData): Promise<string> }

In practice that means signers from Privy, Dynamic, Turnkey, and other embedded wallet providers all work.

// viem local account
import { privateKeyToAccount } from "viem/accounts";
const account = privateKeyToAccount(process.env.WALLET_PRIVATE_KEY as `0x${string}`);
const agent = new HoodLayerAgent({ wallet: account, network: "mainnet" });

// embedded wallet from Privy
const agent = new HoodLayerAgent({ wallet: privySigner, network: "mainnet" });

Methods

agent.pay()

Sends a one-time payment through the x402 flow, covering the whole request-402-pay-retry cycle for you.

const result = await agent.pay(options: PayOptions): Promise<PayResult>

PayOptions

Option Type Required Description
url string Yes URL of the resource you’re paying for
method string No HTTP verb to use (default: "GET")
body object No Body to send with POST requests
headers object No Extra headers for the request
maxAmount number No Ceiling on what you’ll pay, in token base units. The call rejects if the service asks for more.
token string No Address of the token to pay with (default: USDC)

PayResult

Field Type Description
data any The API’s response body, parsed
status number Status code of the HTTP response
headers object Headers from the response
txHash string Hash of the Robinhood Chain transaction
amountPaid number What was actually paid, in token base units
const result = await agent.pay({
  url: "https://api.example.com/v1/data",
  maxAmount: 1_000_000, // bail out if the service wants more than 1 USDC
});

console.log(result.data);   // the response body
console.log(result.txHash); // proof on-chain

agent.subscribe()

Opens an on-chain subscription to a plan, handing billing authority over to the service provider.

const sub = await agent.subscribe(options: SubscribeOptions): Promise<Subscription>

SubscribeOptions

Option Type Required Description
planId string Yes Id of the plan on-chain
maxOveragePerCycle number No Per-cycle cap on metered overage spend
token string No Use a different token than the plan’s default

Returns: a Subscription object. The full structure is documented in Subscriptions.

const sub = await agent.subscribe({
  planId: "0x7f3a...plan_id",
  maxOveragePerCycle: 10_000_000, // allow overage up to 10 USDC
});

console.log(sub.id);            // id of the subscription record on-chain
console.log(sub.status);        // either "ACTIVE" or "TRIAL"
console.log(sub.nextBillingAt); // a Unix timestamp

agent.cancelSubscription()

Ends an active subscription. Happens on-chain, and takes effect right away.

await agent.cancelSubscription(options: { subscriptionId: string }): Promise<void>

agent.listSubscriptions()

Fetches every subscription tied to the agent’s wallet.

const subs = await agent.listSubscriptions(
  options?: { status?: SubscriptionStatus }
): Promise<Subscription[]>

agent.createAllowance()

Sets up a metered spend cap. Details in Allowances.

const allowance = await agent.createAllowance(options: AllowanceOptions): Promise<Allowance>

AllowanceOptions

Option Type Required Description
grantee string Yes Address of the service wallet being authorized
maxAmount number Yes The overall spend cap, in token base units
token string No Which token contract (default: USDC)
expiresAt number No Expiry, as a Unix timestamp

agent.revokeAllowance()

Pulls an allowance back before it expires.

await agent.revokeAllowance(options: { allowanceId: string }): Promise<void>

agent.getAllowance()

Reads the current state of an allowance.

const status = await agent.getAllowance(
  options: { allowanceId: string }
): Promise<Allowance>

Error handling

Every failure case surfaces as a typed error thrown by the SDK:

import {
  InsufficientFundsError,
  PaymentRejectedError,
  AllowanceExhaustedError,
  FacilitatorError,
} from "@hoodlayerorg/sdk/errors";

try {
  await agent.pay({ url: "...", maxAmount: 1_000_000 });
} catch (e) {
  if (e instanceof InsufficientFundsError) {
    console.error("Wallet needs more USDC", e.required, e.available);
  } else if (e instanceof PaymentRejectedError) {
    console.error("Service rejected payment proof", e.reason);
  } else if (e instanceof FacilitatorError) {
    console.error("Facilitator error", e.statusCode, e.message);
  }
}
Error class Cause
InsufficientFundsError The wallet holds less than the required amount
PaymentRejectedError The service turned down the payment proof
MaxAmountExceededError The service asked for more than maxAmount
AllowanceExhaustedError The spend cap has been used up
SubscriptionNotActiveError The subscription is cancelled or paused
FacilitatorError The Facilitator responded with an error
ChainTransactionError A transaction failed on-chain