@grandprotocol/sdk
v0.2.1
Published
The official TypeScript SDK for Grand Protocol, cohesive payment infrastructure for AI agents
Maintainers
Readme
@grandprotocol/sdk
The payment rail for autonomous AI agents, in one import.
Agents rent compute, call paid APIs, and hire other agents. Every one of those services picks a payment protocol, and right now that means x402 or MPP. This SDK lets your agent pay either one through a single pay() call, with every transaction settled and recorded on Robinhood Chain.
You write the request. Grand Protocol negotiates the protocol, checks your spend policy, signs the payment, and hands you back the API response together with a transaction hash you can look up on-chain.
import { GrandProtocol } from "@grandprotocol/sdk";
const grand = new GrandProtocol({
agentId: "research-agent-v2",
apiKey: process.env.GRAND_API_KEY!,
});
const result = await grand.pay({
endpoint: "https://api.dune.com/v1/query/1234/results",
});
result.data; // the response body from Dune
result.protocol; // "mpp" (detected, not configured)
result.amountPaid; // { usdg: 0.002 }
result.txHash; // 0x5e9a...c41f on Robinhood ChainInstall
npm install @grandprotocol/sdk- Node.js 18 or newer (the SDK uses the built-in
fetch, so there are no runtime dependencies) - Ships ESM and CommonJS builds with full TypeScript declarations
What happens inside pay()
The whole point of the SDK is that you never think about the steps below. It is still worth knowing what they are.
- Your agent's request is forwarded to the Grand Protocol gateway along with your
agentId. - The gateway calls the target endpoint. If the service answers with HTTP 402, the gateway reads the response and works out whether the service speaks x402 or MPP. If a service supports both, x402 wins by default because it is lower overhead and fully on-chain.
- Your spend policy is enforced before any money moves. Daily budget, per-call limit, domain allowlist, rate limit. A call that would breach policy is rejected here, not after the fact.
- The payment is constructed and signed. For x402 that is an EIP-712 typed-data signature and a
PAYMENT-SIGNATUREheader. For MPP it is a session, reused across calls where possible. - The original request is retried with payment attached and the service responds normally.
- The settlement is recorded on Robinhood Chain, including the protocol used, the amount, the counterparty, and the policy check result.
You receive the service's response and the on-chain transaction hash in the same object.
Agent wallets
Every agentId maps to a smart contract wallet on Robinhood Chain, deployed deterministically from the ID. The wallet holds USDG and is the source of funds for every payment the agent makes. It is provisioned the first time the agent is seen and returned unchanged after that.
const wallet = await grand.wallet.get();
wallet.address; // 0x7A3f...b1A2
wallet.balance.usdg; // 42.5
// Shorthand when you only need the number
const { usdg } = await grand.wallet.balance();
// Top up from the funding source attached to your account
const receipt = await grand.wallet.fund({ usdg: 50 });
receipt.txHash; // funding transaction on Robinhood Chain
receipt.balance; // { usdg: 92.5 }Wallets are non-custodial. Grand Protocol cannot move funds without a valid transaction signed by the agent's registered key. You can also send USDG straight to the wallet address from any Robinhood Chain wallet.
Spend policies
Policies are set once per agent and enforced by the gateway on every call. There is no per-request budgeting code to write, and no way for a runaway loop to spend past the cap.
await grand.policies.set({
dailyBudget: { usdg: 100 },
perCallLimit: { usdg: 1.0 },
allowedDomains: ["api.dune.com", "api.browserbase.com", "fal.ai"],
blockedDomains: [],
rateLimit: { calls: 500, window: "1h" },
});
const active = await grand.policies.get();| Field | Effect |
| --- | --- |
| dailyBudget | Total USDG the agent may spend in a rolling 24-hour window |
| perCallLimit | Maximum USDG for any single payment |
| allowedDomains | If set, payments are only permitted to these hosts |
| blockedDomains | Hosts that are always refused, even if allowlisted |
| rateLimit | Maximum number of paid calls per window, for example { calls: 500, window: "1h" } |
A payment that fails a policy check throws PolicyViolationError and nothing leaves the wallet. Policy violations are logged on-chain, so an auditor can see what was attempted as well as what was paid.
Transactions
The ledger is the blockchain. The transactions resource is a convenient view over it.
const page = await grand.transactions.list({ limit: 50 });
page.transactions; // Transaction[]
page.total; // count across all pages
page.hasMore; // true if there is another page
// Narrow by protocol and time range
const januaryX402 = await grand.transactions.list({
protocol: "x402",
from: "2026-01-01T00:00:00Z",
to: "2026-01-31T23:59:59Z",
offset: 0,
});
// Look up a single payment
const tx = await grand.transactions.get("0x5e9a3f...c41f");
tx.status; // "confirmed" | "pending" | "failed"
tx.explorerUrl; // direct link to the Robinhood Chain explorer
tx.parentTxHash; // set when this payment was made on behalf of another agentEach transaction record carries the agent, the endpoint, the amount, the protocol, a timestamp, a status, and an explorer link. When an orchestrating agent delegates work to a sub-agent, the sub-agent's payments carry the orchestrator's transaction hash in parentTxHash, so the whole chain of spending is traceable from the top.
Idempotency
Pass an idempotencyKey on any pay() or fund() call to make retries safe. If the same key is seen twice, the gateway returns the original result instead of paying again.
await grand.pay({
endpoint: "https://api.fal.ai/v1/inference",
method: "POST",
body: { model: "flux-pro", prompt: "a lighthouse at dusk" },
idempotencyKey: `job-${jobId}`,
});Privacy mode
By default every payment is fully transparent on Robinhood Chain. For workloads where the counterparty or amount is sensitive, you can opt into zero-knowledge settlement per call.
const result = await grand.pay({
endpoint: "https://api.service.com/resource",
method: "POST",
body: { query: "..." },
privacy: {
mode: "full", // or "confidential_amount"
disclosureKey: complianceOfficerPubKey, // optional
},
});
result.txHash; // a nullifier hash, no identity on-chain
result.privateNote; // encrypted note for your own records
result.disclosureProof; // openable only by the disclosureKey holder| Mode | On-chain visibility |
| --- | --- |
| transparent | Everything public. This is the default when privacy is omitted. |
| confidential_amount | Counterparty visible, amount hidden |
| full | Neither counterparty nor amount visible |
Supplying a disclosureKey lets a named auditor decrypt the payment later without making it public. Private payments are routed through a separate gateway path and still count against your spend policies.
Errors
Every error thrown by the SDK extends GrandError, which exposes a machine-readable code and, where relevant, the HTTP statusCode from the gateway.
import {
GrandProtocol,
GrandError,
AuthenticationError,
InsufficientFundsError,
PolicyViolationError,
RateLimitError,
APIError,
} from "@grandprotocol/sdk";
try {
await grand.pay({ endpoint: "https://api.service.com/resource" });
} catch (err) {
if (err instanceof InsufficientFundsError) {
await grand.wallet.fund({ usdg: 25 });
} else if (err instanceof PolicyViolationError) {
// blocked by dailyBudget, perCallLimit, domain rules, or rateLimit
} else if (err instanceof RateLimitError) {
// back off and retry
} else if (err instanceof GrandError) {
console.error(err.code, err.statusCode, err.message);
}
}| Class | code | Gateway status | When |
| --- | --- | --- | --- |
| AuthenticationError | authentication_error | 401 | The API key is missing, revoked, or wrong |
| InsufficientFundsError | insufficient_funds | 402 | The agent wallet cannot cover the payment |
| PolicyViolationError | policy_violation | 403 | A spend policy rejected the call |
| RateLimitError | rate_limit_exceeded | 429 | The agent's rateLimit window is exhausted |
| APIError | api_error | anything else | Any other non-2xx response from the gateway |
API reference
new GrandProtocol(config)
| Option | Type | Required | Description |
| --- | --- | --- | --- |
| agentId | string | Yes | Stable identifier for this agent. Determines the wallet address. |
| apiKey | string | Yes | Your Grand Protocol API key |
| baseUrl | string | No | Gateway URL. Defaults to https://api.grandprotocol.org. |
The constructor throws immediately if agentId or apiKey is empty.
grand.pay(options): Promise<PayResult>
| Option | Type | Default | Description |
| --- | --- | --- | --- |
| endpoint | string | required | The paid service URL |
| method | "GET" \| "POST" \| "PUT" \| "PATCH" \| "DELETE" | "GET" | HTTP method |
| body | unknown | | Request body, JSON-encoded |
| headers | Record<string, string> | | Extra headers forwarded to the service |
| privacy | PrivacyOptions | | Enable zero-knowledge settlement |
| idempotencyKey | string | | Deduplicate retries |
Returns { status, txHash, amountPaid, protocol, data, privateNote?, disclosureProof? }.
grand.wallet
| Method | Returns |
| --- | --- |
| get() | WalletInfo with agentId, address, and balance |
| balance() | { usdg: number } |
| fund({ usdg, idempotencyKey? }) | FundResult with txHash, amount, and the new balance |
grand.transactions
| Method | Returns |
| --- | --- |
| list({ limit?, offset?, protocol?, from?, to? }) | { transactions, total, hasMore } |
| get(txHash) | A single Transaction |
grand.policies
| Method | Returns |
| --- | --- |
| get() | The active SpendPolicy for this agent |
| set(policy) | The saved SpendPolicy |
All option and result types are exported: GrandConfig, PayOptions, PayResult, PrivacyOptions, WalletInfo, WalletBalance, FundOptions, FundResult, Transaction, TransactionList, ListTransactionsOptions, and SpendPolicy.
About the settlement layer
Robinhood Chain is an Ethereum Layer 2 built on the Arbitrum stack, with 100ms block times and settlement to Ethereum for security. It is fully EVM-compatible, so existing wallets, indexers, and x402 tooling work without changes. USDG is the settlement token for every Grand Protocol payment.
| | | | --- | --- | | Chain ID | 4663 (mainnet), 46630 (testnet) | | Gas token | ETH | | Settlement token | USDG (ERC-20) |
What this SDK is not
- Not an agent framework. Bring your own orchestration. This package only handles paying for things.
- Not a wallet you have to manage. Keys are registered per agent and never pass through your application code.
- Not a fee layer. Grand Protocol takes no percentage of transaction volume.
Documentation · Dashboard · grandprotocol.org
MIT licensed.
