Installation

npm install @opusprotocol/sdk

OpusProtocolAgent

import { OpusProtocolAgent } from "@opusprotocol/sdk";

const agent = new OpusProtocolAgent(config: OpusProtocolAgentConfig);

OpusProtocolAgentConfig

Option Type Required Description
wallet Signer Yes The signing account: viem, ethers, or anything that implements the interface described below
network "mainnet" \| "testnet" Yes Which Robinhood Chain network to target
rpcUrl string No Override the Robinhood Chain RPC endpoint
facilitatorUrl string No Point at a different Facilitator (defaults to the one Opus Protocol hosts)
defaultToken string No Address of the token used when a call does not name one (defaults to USDG)

Wallets

Two kinds of value work for wallet:

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

Because the interface is that small, signers from embedded wallet providers (Privy, Dynamic, Turnkey, and others like them) can be passed in with no adapter.

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

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

Methods

agent.pay()

Makes a single x402 payment. One call covers the whole sequence: the initial request, the 402 response, the payment itself, and the retried request.

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

PayOptions

Option Type Required Description
url string Yes Address of the resource to purchase
method string No HTTP method; "GET" if omitted
body object No Payload sent with POST requests
headers object No Extra headers to include on the request
maxAmount number No Upper bound in token base units; if the service asks for more, the call throws
token string No Address of the token to pay in (defaults to USDG)

PayResult

Field Type Description
data any The API’s response body, parsed
status number HTTP status returned by the service
headers object Headers on the response
txHash string Hash of the Robinhood Chain transaction
amountPaid number What was actually charged, in token base units
const result = await agent.pay({
  url: "https://api.example.com/v1/data",
  maxAmount: 1_000_000, // reject any charge above 1 USDG
});

console.log(result.data);   // the response body
console.log(result.txHash); // the payment, written to the chain

agent.subscribe()

Opens a subscription to a plan on-chain. Subscribing hands the provider the authority to bill the wallet each cycle.

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

SubscribeOptions

Option Type Required Description
planId string Yes The plan’s on-chain identifier
maxOveragePerCycle number No The most the provider may charge for metered overage in any one cycle
token string No Use this token instead of the plan’s default

Returns: a Subscription. The complete field list is documented in Subscriptions.

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

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 a subscription that is currently active. The change is recorded on-chain and takes effect at once.

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

agent.listSubscriptions()

Fetches every subscription tied to the agent’s wallet, optionally filtered by status.

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

agent.createAllowance()

Sets up a capped, metered spending grant. Allowances explains how the model works.

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

AllowanceOptions

Option Type Required Description
grantee string Yes Wallet address of the service allowed to draw on the grant
maxAmount number Yes Hard ceiling on total spend, in token base units
token string No Address of the token contract (defaults to USDG)
expiresAt number No Unix timestamp after which the allowance is no longer valid

agent.revokeAllowance()

Cancels an allowance before it would otherwise expire.

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

Each way a call can fail maps to its own error class:

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

try {
  await agent.pay({ url: "...", maxAmount: 1_000_000 });
} catch (e) {
  if (e instanceof InsufficientFundsError) {
    console.error("Wallet needs more USDG", 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 does not hold enough to cover the payment
PaymentRejectedError The service refused the proof of payment
MaxAmountExceededError The price quoted by the service was higher than maxAmount
AllowanceExhaustedError Nothing remains of the allowance’s cap
SubscriptionNotActiveError The subscription has been paused or cancelled
FacilitatorError The Facilitator responded with an error
ChainTransactionError The on-chain transaction did not succeed