Agent SDK
Full reference for OpusProtocolAgent, the class on the paying side: one-time payments, subscriptions, and allowances from a single wallet.
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 |