
Research
/Security News
OpenAPI React Query Codegen Compromised in Mini Shai-Hulud npm Supply Chain Attack
Ten malicious OpenAPI React Query Codegen versions were published to npm in the Mini Shai-Hulud attack, all with valid provenance.
Let AI agents pay for APIs. x402 USDC payments two ways: sign locally with your own key (no account, no signup), or use ArisPay delegated custody with server-enforced spend limits.
Let AI agents pay for APIs. The ArisPay SDK + CLI for x402 USDC payments.
Two ways to pay:
payFetchLocal({ privateKey }) signs EIP-3009 payments in your process with any funded key. No ArisPay account, no API key, no provisioning — npm install payagent is the whole setup. Settlement runs through whatever facilitator the seller uses (any upstream-compatible x402 facilitator, including facilitator.arispay.app — source).Honest trade-off: spend limits, domain allowlists, suspension, and the payment feed are server-side features of the delegated model and do not exist in local-signer mode — the only guardrail is an optional client-side per-transaction cap. Use a dedicated low-balance wallet for local signing.
npm install payagent
Requires Node.js >= 18.
No key yet? Generate one and get a deposit address (nothing is stored; save the key):
npx payagent wallet new
Send USDC on Base to the printed address, then pay in one step:
PAYAGENT_PRIVATE_KEY=0x… npx payagent pay https://api.example.com/premium
payagent wallet address re-prints the deposit address for a key you already have.
Library equivalent:
import { payFetchLocal } from "payagent";
const fetch402 = payFetchLocal({
privateKey: process.env.PRIVATE_KEY!,
maxPerTxBaseUnits: "1000000", // optional client-side cap: $1.00 USDC
});
const res = await fetch402("https://api.example.com/premium");
The vercel and langchain tool integrations accept either mode — pass
{ privateKey } for local signing or { arispayUrl, apiKey } for delegated.
One command. No dashboard trip, no separate init, no separate agent create, no manual funding step.
npx payagent pay https://api.example.com/premium
On a cold machine, pay self-bootstraps in order:
default with conservative defaults ($5/tx, $20/day, $100/month, Base mainnet, URL hostname allowlisted). Persisted to ~/.payagent/config.json, so subsequent runs skip this step.X-PAYMENT header.Progress goes to stderr; the HTTP response body is written to stdout, so you can pipe it:
npx payagent pay https://api.example.com/premium | jq .
Overrides (all optional) let you tune the first-run defaults:
npx payagent pay <url> \
--agent hermes \ # name the agent (defaults to `default`)
--per-tx 0.50 --daily 10 --monthly 100 \# dollars; cents stored server-side
--network base \ # base | base-sepolia | ethereum | polygon
--domains api.example.com,api.example.net \
--method POST --body '{"foo":"bar"}' \
--amount 25 # preset Coinbase Onramp amount (USD)
For advanced setups you can still run the gates manually:
npx payagent init # OAuth device-code sign-in
npx payagent agent create --name hermes \
--per-tx 0.50 --daily 10 --monthly 100 # amounts in dollars
npx payagent agent fund hermes --hosted 25 # Coinbase Onramp link for $25
The CLI and the @arispay/payagent-mcp server share the same ~/.payagent/config.json, so an agent created on the command line is immediately available to Claude Desktop or any other MCP host.
import { launchAgent } from 'payagent';
const hermes = await launchAgent({
name: 'hermes',
limits: { perTx: 50, daily: 1000, monthly: 10000 }, // cents
allowedDomains: ['api.example.com'],
});
console.log(`Fund this wallet with USDC on Base: ${hermes.walletAddress}`);
await hermes.waitUntilFunded();
const res = await hermes.fetch('https://api.example.com/premium');
const data = await res.json();
For finer-grained control, the low-level DelegationClient + payFetchDelegated primitives are still exported and used by launchAgent under the hood:
import { DelegationClient, payFetchDelegated } from 'payagent';
const client = new DelegationClient('https://api.arispay.app', process.env.ARISPAY_API_KEY);
const agent = await client.createX402Agent({
name: 'my-agent',
maxPerTx: 100,
maxDaily: 1000,
maxMonthly: 10000,
allowedDomains: ['api.example.com'],
});
await client.pollUntilFunded(agent.agentId);
const fetch402 = payFetchDelegated({
arispayUrl: 'https://api.arispay.app',
apiKey: agent.apiKey,
});
const res = await fetch402('https://api.example.com/premium');
| Command | Purpose |
|---|---|
payagent init | Browser-based OAuth device-code flow. Saves the developer API key to ~/.payagent/config.json (mode 0600). |
payagent status / payagent doctor | Show local readiness: API target, key validity, cached agents, balances, and next command. |
payagent whoami | Show the current developer key prefix, base URL, and config path. |
payagent logout | Clear the developer key. Local agent records are kept. |
| `payagent agent create --name N --per-tx N --daily N --monthly N [--domains a,b] [--network base | base-sepolia |
payagent agent fund NAME | Print the funding address, render a QR, and poll until the wallet first receives USDC. |
payagent agent balance NAME | Show current balance + fundedAt. |
payagent agent list | List locally-cached agents. |
payagent agent remove NAME | Drop an agent from the local cache (the server-side agent remains). |
payagent pay URL [--agent N] [--method GET/POST/…] [--body STR] | Make a single paid request through payFetchDelegated. |
Amounts on the CLI are in dollars (5, 5.00, or "$5.00" all mean 500 cents). Internally everything is integer cents, matching the ArisPay API.
Env overrides respected everywhere: ARISPAY_API_KEY, ARISPAY_URL, PAYAGENT_CONFIG_DIR.
For end-user-friendly funding — no crypto wallet required — ArisPay can return a hosted Coinbase Onramp URL instead of a raw wallet address. The user opens the URL, pays with card or Apple Pay, and Coinbase deposits USDC straight into the agent's CDP wallet.
import { launchAgent, HostedTopupNotConfiguredError } from 'payagent';
const hermes = await launchAgent({
name: 'hermes',
limits: { perTx: 50, daily: 1000, monthly: 10000 },
});
try {
const link = await hermes.getFundingLink({ amount: 50 });
console.log(`Pay here: ${link.fundingUrl}`);
// Expires at link.expiresAt, valid for ~2 hours.
await hermes.waitUntilFunded();
} catch (err) {
if (err instanceof HostedTopupNotConfiguredError) {
// Deployment hasn't set ARISPAY_ONRAMP_PROVIDER — fall back to
// the plain wallet address.
console.log(`Send USDC on base to ${hermes.walletAddress}`);
await hermes.waitUntilFunded();
} else {
throw err;
}
}
Or via the CLI:
npx payagent agent fund hermes --hosted 50
Requires ARISPAY_ONRAMP_PROVIDER=coinbase + COINBASE_ONRAMP_APP_ID
on the API deployment. Without them, the --hosted flag falls back to
the manual path with a clear message.
The x402 protocol uses HTTP status code 402 for machine-to-machine API payments:
402 Payment Required with payment requirements (chain, amount, recipient).transferWithAuthorization — a gasless USDC transfer authorization.maxPerTx, maxDaily, maxMonthly, allowedDomains), then signs via the CDP-managed wallet.X-PAYMENT header.Key properties:
DelegationClient — provision and monitor agentsconst client = new DelegationClient('https://api.arispay.app', process.env.ARISPAY_KEY);
const agent = await client.createX402Agent({
name: 'hermes-prod',
agentType: 'hermes',
maxPerTx: 100, // cents
maxDaily: 1000,
maxMonthly: 10000,
allowedDomains: ['api.example.com'],
network: 'base', // default
});
// agent.agentId, agent.walletAddress, agent.apiKey (returned ONCE)
// Wait for the wallet to be funded with USDC.
await client.pollUntilFunded(agent.agentId);
// Or check manually:
const balance = await client.getBalance(agent.agentId);
// { walletAddress, usdcBalance, network, fundedAt }
Supported network values: base (default, mainnet), base-sepolia, ethereum, polygon.
The apiKey returned by createX402Agent is the credential for this agent only — ArisPay stores only its SHA-256 hash and cannot recover it. Store it securely.
payFetchDelegated(config) — drop-in fetchconst fetch402 = payFetchDelegated({
arispayUrl: 'https://api.arispay.app',
apiKey: agent.apiKey,
});
const res = await fetch402('https://api.example.com/data');
Works like native fetch. On 402, payagent asks ArisPay to sign via CDP, then retries. If ArisPay rejects (limit breach, disallowed domain, etc.), throws PaymentRejectedError.
getUSDCBalance(address, chain?, rpcUrl?) — direct on-chain readimport { getUSDCBalance, formatUSDC } from 'payagent';
const raw = await getUSDCBalance(agent.walletAddress); // default chain: 'base'
const raw2 = await getUSDCBalance(agent.walletAddress, 'base');
console.log(formatUSDC(raw), 'USDC');
Utility that reads USDC balance directly from any EVM RPC — handy for sanity-checking wallet state independent of ArisPay's balance endpoint.
| Chain | Network ID | Notes |
|---|---|---|
| Base | eip155:8453 | Default — mainnet, lowest fees |
| Base Sepolia | eip155:84532 | Testnet |
| Ethereum | eip155:1 | Mainnet |
| Polygon | eip155:137 | Mainnet |
import {
PaymentRejectedError, // ArisPay denied signing, or server returned 402 after payment
InvalidRequirementsError, // Could not parse the 402 response
PayAgentError, // Base class
} from 'payagent';
import { createPayAgentTool } from 'payagent/vercel';
import { generateText } from 'ai';
const payTool = createPayAgentTool({
arispayUrl: 'https://api.arispay.app',
apiKey: process.env.ARISPAY_AGENT_KEY,
});
const { text } = await generateText({
model: yourModel,
tools: { pay_api: payTool },
prompt: 'Get the premium forecast from https://weather-api.example.com/forecast',
});
Requires peer dependencies: ai, zod.
import { createPayAgentTool } from 'payagent/langchain';
const payTool = createPayAgentTool({
arispayUrl: 'https://api.arispay.app',
apiKey: process.env.ARISPAY_AGENT_KEY,
});
Requires peer dependency: @langchain/core.
Use @arispay/payagent-mcp to add payment capabilities to Claude Desktop, Cursor, or any MCP client.
v2.0 removed the 1.x self-custody API (payFetch, PayAgent). Local signing returned as a first-class mode in 2.13 with a new surface: payFetchLocal({ privateKey }) and PAYAGENT_PRIVATE_KEY (see the local-signer quick start above). The 1.x names are gone for good.
Migration:
payFetch({ privateKey }) with payFetchLocal({ privateKey }) (self-custody) or payFetchDelegated({ arispayUrl, apiKey }) (delegated custody with server-enforced limits).new PayAgent({ privateKey, budget, maxPerRequest }) with DelegationClient.createX402Agent({ maxPerTx, maxDaily, maxMonthly, ... }) to provision, then payFetchDelegated to transact — or use payFetchLocal with maxPerTxBaseUnits for a client-side cap.BudgetExceededError, UnsupportedChainError, and DomainNotAllowedError are gone — their equivalents are now server-side rejections surfaced as PaymentRejectedError.MIT
FAQs
Let AI agents pay for APIs. x402 USDC payments two ways: sign locally with your own key (no account, no signup), or use ArisPay delegated custody with server-enforced spend limits.
The npm package payagent receives a total of 1,063 weekly downloads. As such, payagent popularity was classified as popular.
We found that payagent demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Research
/Security News
Ten malicious OpenAPI React Query Codegen versions were published to npm in the Mini Shai-Hulud attack, all with valid provenance.

Security News
Socket joins more than 100 technology, cybersecurity, and financial organizations calling for a global surge in cyber defense.

Product
Enterprise security teams can now detect malware, credential theft, suspicious network activity, and risky updates across Microsoft Edge extensions.